/**
 * BrandBigData.com Inc.
 * Copyright (c) 2016 All Rights Reserved.
 */
package com.qibao.toolkit.protocol.protocols;

import com.qibao.toolkit.protocol.exception.ProtocolException;
import com.qibao.toolkit.protocol.facade.Protocol;
import com.qibao.toolkit.protocol.facade.ProtocolConfiguration;
import com.qibao.toolkit.protocol.facade.Request;
import com.qibao.toolkit.protocol.facade.Response;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qibao.toolkit.util.JsonUtils;
import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

/**
 * username和password从queryParameters里面取
 * sql从requestBody里面取
 *
 * @author xumin
 * @version $Id:DatabaseProtocol.java, v0.1 2017/12/23 17:30 xumin
 */
@Component
public class DatabaseProtocol implements Protocol {

    private static final Logger logger = LoggerFactory.getLogger(DatabaseProtocol.class);
    private ConcurrentMap<String, DataSource> dataSourceMap = Maps.newConcurrentMap();

    @Override
    public Response sendRequest(Request request, ProtocolConfiguration protocolConfiguration) throws ProtocolException {
        String url = request.getUrl();
        if (url.endsWith("/")) {
            request.setUrl(url.substring(0, url.length() - 1));
        }
        String key = getDataSourceKey(request);
        DataSource dataSource = dataSourceMap.computeIfAbsent(key, k -> getDataSource(request));
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(request.getRequestBody());
            ResultSet resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            List<Map<String, Object>> resultList = Lists.newArrayList();
            while (resultSet.next()) {
                Map<String, Object> map = Maps.newHashMap();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    map.put(metaData.getColumnLabel(i), resultSet.getObject(i));
                }
                resultList.add(map);
            }
            Response response = new Response();
            response.setContent(JsonUtils.writeValueAsString(resultList));
            response.setParsedContent(JsonUtils.writeValueAsString(resultList));
            response.setSuccess(true);
            return response;
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
            throw new ProtocolException(e.getMessage());
        } finally {
            if (null != connection) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }

    }

    private DataSource getDataSource(Request request) {
        String url = request.getUrl();
        if (url.endsWith("/")) {
            request.setUrl(url.substring(0, url.length() - 1));
        }
        DataSourceFactory dataSourceFactory = new DataSourceFactory();
        Properties properties = new Properties();
        properties.put("initialSize", "5");
        properties.put("maxIdle", "1");
        properties.put("minIdle", "1");
        properties.put("maxActive", "5");
        properties.putAll(request.getQueryParameters());
        properties.put("url", request.getUrl());
        properties.put("validationQuery","SELECT 1 FROM DUAL");
        properties.put("timeBetweenEvictionRunsMillis","60000");
        properties.put("minEvictableIdleTimeMillis","300000");
        properties.put("testOnBorrow","true");
        properties.put("testWhileIdle","true");
        properties.put("testOnReturn","false");
        logger.info("getDataSource by properties: {}", properties);
        try {
            return dataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private String getDataSourceKey(Request request) {
        return request.getUrl();
    }
}
